iT邦幫忙

2022 iThome 鐵人賽

DAY 13
0
Modern Web

真的好想離開 Vue 3 新手村 feat. CompositionAPI系列 第 13 篇

真的好想離開 Vue 3 新手村 - Day 13: v-on 語法、修飾符與找不到的 console.log

  • 分享至 

  • xImage
  •  

前言

今天這篇會先介紹 v-on 的語法和修飾符,熟悉的人可以考慮跳過這個部份。

再來會提到自己在學習 v-on 時的疑惑:

「為什麼不能在 inline handler 使用 console.log?」

如果你也很好奇,就一起來看看吧! (不好奇的話就跳過吧哈哈)

最後會補充 .passive 修飾符,這個修飾符對應到 addEventListener 的 passive option,是我自己之前比較陌生的部份!

Outline

  • v-on 快速通關:語法簡介
  • 為什麼不能在 inline handler 調用 console.log
  • 如何設定在 Vue 模板可使用 console.log
    • 利用 app.config.globalProperties 自定義所有模板都可以取到的全域屬性
  • 補充:來點原生 Javascript 之 .passive 修飾符

v-on 快速通關

Syntax

  • v-on:

    • v-on 指令是用來為元素綁定事件監聽器
    • v-on 的部份可以縮寫為 @。
  • Argument

    • 原生事件:對應原生 Event 底下的 type 屬性,鍵的名稱可以參考 MDN - Events
    • 自訂事件:可以透過 emit 來發送自定義事件
  • Modifiers
    Vue 針對常用的事件邏輯處理,提供對應的修飾符(modifier),開發者不用一直寫重複的程式碼,也讓 handler 內的程式碼變得更簡潔,並且修飾符可以串接使用。

    • .stop:阻止事件冒泡,相當於 event.stopPropagation()
    • .prevent:取消事件的預設行為,相當於 event.preventDefault()
    • .self:只在綁定監聽器的元素&觸發監聽器的元素相等才執行,即 event.target === event.currentTarget

    還有對應到 addEventListener 三個 option 的修飾符:

    addEventListener(type, listener, options = {
        capture: true,
        once: true,
        passive: true,
    })
    
    • .capture: 切換成捕獲模式(capture)
    • .once:只觸發一次,觸發後就自動移除監聽
    • .passive:告訴瀏覽器這個 handler 內不會呼叫 event.preventDefault(),通常用在 touch 相關的事件,可以讓行動裝置上的頁面滑動更流暢;不可以和 .prevent 修飾符同時使用。(發現自己對 .passive 並不熟悉,這部份補充在最後)

    按鍵修飾符:監聽鍵盤相關事件時,通常需要確認使用的按鍵,在 Vue 可以透過 Key modifier 做到,鍵的名稱可以參考 MDN - Key values for keyboard events。
    針對常用的按鍵提供別名:

    • .enter
    • .tab
    • .delete (包含 "Delete" 和 "Backspace")
    • .esc
    • .space
    • .up
    • .down
    • .left
    • .right

    系統鍵修飾符:和一般按鍵不同,事件觸發時,系統鍵必須處於按下的狀態,通常會跟其他按鍵組合使用。

    • .ctrl
    • .alt
    • .shift
    • .meta

    滑鼠按鍵修飾符:.left、.right、.middle 可以配合 mouseEvent 使用。

    嚴格修飾符 .exact:想要嚴格符合設定才會觸發事件,可以使用 .exact 來做修飾,以 click 為例:

    • @click.exact : 什麼按紐都不按點擊按鈕才會觸發事件。
    • @click : 在點擊時按住任何按鈕都會觸發事件。
  • Value
    v-on 根據綁定的 value 不同分為兩種:

    1. method handler:value 指向 script 內定義的 method
      • 會自動傳入 event 作為第一個參數
      <button @click="warn">
        Submit
      </button>
      
      function warn(event) {
        // 可以取到原生的 event 物件
        if (event) {
          event.preventDefault()
        }
        window.alert("現在還不能送出");
      }
      
    2. inline handler:指令綁定要執行的 Javascript 內容
      • 一般使用
      <button @click="count++">+1</button>
      <button @click="userInput = ''">Reset</button>
      
      • 呼叫需要傳參數的 method
      function warn(message, event) {
        // 可以取到原生的 event 物件
        if (event) {
          event.preventDefault()
        }
        alert(message)
      }
      
      1. 一般函式 + $event
      <button @click="warn('Form cannot be submitted yet.', $event)">
        Submit
      </button>
      
      1. 箭頭函式 + event
      <button @click="(event) => warn('Form cannot be submitted yet.', event)">
        Submit
      </button>
      

為什麼不能在 inline handler 用 console.log

在 Vue 模板測試 v-on event handler,如:在父元件監聽子元件 emit 事件時,會很直覺想用 inline handler 搭配 console.log() 去印東西,確認有監聽到事件。

像這樣:

<template>
    <button @click="console.log('點擊了按鈕')">按鈕</button>
</template>

或是這樣:

<template>
    <BaseInput
        :modelValue="userName"
        @update:modelValue="console.log('輸入了名字')"
      />
</template>

結果就是收到無數的報錯QQ
像這樣:

原因

這是因為 Vue 預設 template 中的表達式只能使用部份的全域物件,例如:Number, Array, Math, Date 等等,其中不包括 console 跟 window!

所以每次在 template 讀到 console.log 時, Vue 無法取用到全域的 console,它會在元件內部找尋 console 這個變數,所以相關報錯通常會是無法讀取 undefined(reading 'log')。
同理,在 Vue 預設的情境下,我們也沒辦法在 template 調用 window.alert(),或 window 底下相關的變數或函式。

但我們可以透過 app 實例調整設定,做法很簡單。

最後附上 Vue template 可以使用的完整 global 清單如下: 原始碼連結

const GLOBALS_WHITE_LISTED =
  'Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,' +
  'decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,' +
  'Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt'

設定在 Vue 模板可使用 console.log

我們可以透過 app.config.globalProperties 將 console 註冊為所有元件實例都可以取用到的全域物件。

在 main.js 中加入這段程式碼,就可以在所有 Vue template 中使用 console.log。

app.config.globalProperties.console = console;

記得要在 app 實例建立之後,才可以透過 .config 去設定。

const app = createApp(App);

app.config.globalProperties.console = console;

app.mount("#app");

其他注意事項

  1. 如果全域屬性和元件上的屬性有衝突,會以元件的屬性為優先。
  2. 這個是 Vue 3 的用法,如果你使用的是 Vue 2 ,要透過 Vue.property 去設定全域屬性。

補充

來點原生 Javascript 之 .passive 修飾符

.passive 修飾符會對應到 addEventListener 的 passive option (預設值為 false):

addEventListener(type, listener, {
    passive: true,
})

將 passive 選項設置為 true 就是在告訴瀏覽器:這個事件 handler 裡面不會呼叫 event.preventDefault(),你可以直接響應預設行為。

這樣有什麼好處?

可以優化效能,提昇使用者體驗。
因為瀏覽器事先不知道 handler 裡面是否會呼叫 event.preventDefault() 來阻止預設行為,所以瀏覽器需要先執行一次 handler,花時間確定裡面有沒有呼叫 event.preventDefault() ,才決定是否要響應預設行為。

適合拿來優化高頻率觸發的事件監聽器,例如:手機的 touch 事件,瀏覽器就不用在每次使用者開始滑動的時候,花時間重新確認 handler 裡面是否呼叫 event.preventDefault(),導致滑動上的微卡頓。

想知道效果可以看這個影片:
Yes

  • 左邊:一般預設
  • 右邊:啟用 passive option

根據 MDN 的說法,除了 Safari 和 IE,其他多數瀏覽器已經將 Window、Document 和 Document.body 在 wheel、mousewheel、touchstart 和 touchmove 事件監聽中 passive 選項預設為 true。

那 scroll 事件呢?這個事件感覺也很適合使用 passive。

The basic scroll event cannot be canceled, so it does not need to be set passive.

scroll 事件的預設行為本身是不能取消的,所以不需要對這類事件監聽器啟用 passive 選項。

想知道 .passive 修飾符的作用可以看以下資料:

參考資料


上一篇
真的好想離開 Vue 3 新手村 - Day 12: 認識 nextTick 與 DOM 響應更新時機 feat. template ref
下一篇
真的好想離開 Vue 3 新手村 - Day 14: style scoped 原理與特殊選擇器 :deep()&:slotted()
系列文
真的好想離開 Vue 3 新手村 feat. CompositionAPI 共 31 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言